perm filename TCPJS.MAC[IP,SYS] blob sn#680222 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-TCP>TCPJS.MAC.40303 29-Apr-82  7:56:33, Edit by CLYNN
; Work on packetizing
;<403-TCP>TCPJS.MAC.40301 29-Jan-82 15:03:44, Edit by CLYNN
; Updated for TCP release 3, STAT functions added: symbolic, TVT, conn
; MAXSTO moved to STG as TCPPTM
;[BBNF]<401-TCP>TCPJS.MAC.153, 10-Jul-81 12:32:00, Ed: CLYNN
; Fix: wait bit usage in MAKBFR & FREBFR
;<401-TCP>TCPJS.MAC.148,  4-Apr-81 17:18:53, Edit by TAPPAN
; Put multinet stuff in master file under a conditional

	SEARCH	INPAR,TCPPAR,PROLOG
IFN MNET,<
	SEARCH	MNTPAR
>
	TTITLE	TCPJS
	SUBTTL	TCP JSYS Service Routines, William W. Plummer, 25FEB77
	SWAPCD
COMMENT	!

	This file contains the routines which service user
	calls on the TCP.  Executed in non-Job0 process context.

* .SEND ...  3 ...... SEND JSYS
  SEND1 ...  4 ...... SEND, second phase
  SETURG ..  7 ...... Set send urgent pointer
* .RECV ...  8 ...... RECV JSYS
  RECV1 ...  9 ...... RECV, second phase
* .OPEN ... 11 ...... OPEN JSYS
  OPEN1 ... 12 ...... OPEN, second phase
* .CLOSE .. 14 ...... CLOSE JSYS
* CLOSE1 .. 15 ...... CLOSE, second phase
* .ABORT .. 16 ...... ABORT JSYS
  ABORT1 .. 16 ...... ABORT, second phase
* ABTJCS .. 17 ...... Abort JCNs for fork and inferiors
  ABTJC1 .. 17 ...... Second phase of above
  ABTJCN .. 18 ...... Abort a JCN
* ABTTCB .. 19 ...... Abort a connection
  ABTPTR .. 20 ...... ADJBP simulation for ABTTCB
* TCPABT .. 20 ...... ABORT done test for scheduler
* .STAT ... 21 ...... STAT JSYS
  STATS ... 22 ...... STAT, return TCP statistics
  STAT1 ... 23 ...... STAT, second phase
  STATNM .. 24 ...... STAT, from symbolic input
  SRCH .... 25 ...... STAT, Binary lookup routine for STATNM
            26 ...... STAT, tables of names, counts, and byte pointers
* .CHANL .. 27 ...... CHANL JSYS
  CHANL1 .. 28 ...... CHANL, second phase
* .SCSLV .. 29 ...... SCSLV JSYS
  SCSLV1 .. 29 ...... SCSLV, second phase
* TATNVT .. 30 ...... TVT portion of ATNVT JSYS
  TATNV1 .. 31 ...... ATNVT, second phase

  ACTTCB .. 33 ...... Activate a connection
  CHKARG .. 35 ...... Check arguments to a TCP JSYS
  CHKJCN .. 39 ...... Check validity of a JCN
  GETJCN .. 39 ...... Assign a JCN
  RETJCN .. 40 ...... Release a JCN
  MAKBFR .. 41 ...... Form a buffer descriptor block
  FREBFR .. 44 ...... Release resources used by a buffer
	!
STSFLG==TCP%IX!TCP%NI!TCP%NT!TCP%SD!TCP%ST!TCP%SY!TCP%TV ; Frequent constant

; .SEND		Send a buffer

;T1/	Flags,,JCN (or Pointer to Connection Descriptor)
;T2/	Pointer to buffer header
;T3/	Timeout (in seconds) (0 is infinite)
;T4/	RX parameters
;
;	SEND
;Ret+1:	 Error, Code in T1
;Ret+2:	Success

.SEND::	MCENT			; Enter monitor context
	TXNE T1,<STSFLG!TCP%FS!TCP%PS!TCP%VT>&↑-<TCP%JS!TCP%WT!TCP%HP>
	  JRST TCPILP		; Illegal control bit
	XMOVEI T1,SEND1		; Routine to call via CHKARG
	CALL CHKARG		; Check arguments, set TCB, call SEND1
	JUMPL T1,TCPERR		; Error.
	UMOVE T1,T1		; Get the Flags
	TLNN T1,(TCP%WT)	; Supposed to wait?
	  JRST SKMRTN		; No.  Give immediate skip return
SENDW:	LOAD T1,BIDX,(BFR)	; Buffer Done Flag Index
	LOAD T2,TERRF,(TCB)	; Error Flag index
	ROT T2,-<WID(TERRF)>	; Put in high bits of T2
	LSHC T1,↑D18+<WID(TERRF)> ; Build bfr,err,,INTOOT
	HRRI T1,INTOOT		; Select SEND Done Test routine
	MDISMS			; Wait for either to come on
	JN TERR,(TCB),TCPBER	; Jump if there was an error
	JN TSUOP,(TCB),TCPSKP	; That's all if connection open
	LOAD T1,TOPNF,(TCB)	; Get ID of Open Flag for this TCB
	LOAD T2,TERRF,(TCB)	; Error Flag index
	ROT T2,-<WID(TERRF)>	; Put in high bits of T2
	LSHC T1,↑D18+<WID(TERRF)> ; Build opn,err,,INTZOT
	HRRI T1,INTZOT		; Select Close Done Test
	MDISMS
	JE TERR,(TCB),TCPSKP

TCPBER:	CALL FREBFR		; Release resources used in buffer
	LOAD T1,TERR,(TCB)	; Pick up the error code

TCPERR:	ANDI T1,-1		; Save just the error code
TCPERO:	UMOVEM T1,T1		; Pass to user
	RETERR			; Give no-skip return

TCPILP:	HRROI T1,ELP+↑D1	; Illegal parameter (control bit)
	JRST TCPERR

TCPSKP:	CALL FREBFR		; Release resources
	SMRETN			; And give skip return

; SEND1(TCB)		Second phase of SEND

;T1/	JCN specified by caller
;TCB/	(Extended) Pointer to locked connection block
;			NOINT
;	CALL SEND1
;Ret+1:	Always, T1 has 0 and BFR has the buffer, or T1 has -1,,error

SEND1:	JN TTVT,(TCB),SEND8	; Not allowed for TVTs
	LOAD T3,TSSYN,(TCB)
	CAIE T3,NOTSYN
	CAIN T3,FINSNT		; Closed or closing?
	  JRST SEND6		; Give error
	SETZ T2,		; Not allow options from CDB here
	CALL ACTTCB		; Try to activate the TCB (JCN in T1)
	JUMPL T1,SEND6		; Can't

	XCTU [HRRZ T2,2]	; Get user buffer header address
	SETZ T1,
	JE TNUFM,(TCB),SEND3	; Skip if old format
	UMOVE T1,.TCPBI(T2)	; Get IP info
	UMOVE T2,.TCPBO(T2)	; Get user option addresses word
	TRNE T1,777		; Specified?
	  STOR T1,TTOS,(TCB)	; Yes, Save type of service
	HLRS T1
	TRNE T1,.RTJST(-1,PITTL) ; User specify non-zero time?
	  STOR T1,TTTL,(TCB)	; Yes, Save Time to live
	LSH T1,↑D<-18+2>	; Top two bits
	TRNE T1,3
	  STOR T1,TIFDF,(TCB)	; Don't fragment

	MOVE T1,T2		; Option addresses
SEND3:
; Should options be synchronous or asynchronous??
	SKIPE T1		; Have options?
	 CALL TCPUOP		; Yes
	  JUMPL T1,SENDX	; Error in options

	CALL MAKBFR		; Make a buffer descriptor
	SKIPGE BFR,T1		; Error?
	  EXIT SENDX		; Yes.  Code in T1.

	UMOVE T3,T3		; Get the Send Timeout from user
	JUMPE T3,SEND4		; He says infinite.  Don't set it.
	CAMLE T3,TCPPTM		; Be sure it is reasonable for add to TODCLK
	   MOVE T3,TCPPTM
	IMULI T3,↑D1000		; Convert to milliseconds
	STOR T3,TSTO,(TCB)	; Set new value in TCB
SEND4:

	UMOVE T1,T4		; Get Retrans. parameter word
	CALL RXPARS		; Change them in TCB

	MOVE T1,BFR		; What to Enqueue
	XMOVEI T2,TCBSBQ(TCB)	; Queue head for send buffers
	CALL NQ			; Enqueue it for Packetizer.

	LOAD T1,BICNT,(BFR)	; Initial count
	LOAD T2,TSBYT,(TCB)	; Currently queued for PZ
	ADD T2,T1
	STOR T2,TSBYT,(TCB)	; More...

	MOVE T1,BFRFLG(BFR)	; Get the buffer flags
	TXNN T1,TCP%UR		; URGENT send?
	  JRST SEND43		; No.
	CALL SETURP		; Yes.  Set the send urgent pointer
SEND43:

	LOAD T1,TSLFT,(TCB)	; Current Send Left
	LOAD T2,TSSEQ,(TCB)	; Current Send Sequence
	LOAD T3,TSWND,(TCB)	; Current Send Window
	ADD T3,T1		; Current Right
	MODSEQ T3
	CALL CHKWND		; See if there is space in the window
	JUMPE T1,SEND5		; Jump if not.  Recv'd ACK will restart.
	$SIGNL(PZ,0)		; Make Packetizer run now
SEND5:

	TDZA T1,T1		; Say OK to caller
SEND6:	  HRROI T1,ELP+↑D12	; "Connection Closing"
SENDX:	RET

SEND8:	HRROI T1,ELP+↑D30	; Only internet fork can run TVTs
	RET

; SETURP		Set up the send urgent pointer

; An URGENT send is being done and the value of the send urgent pointer
; must be computed.  This is done by adding up all the queued data
; (on the send buffer queue) to get the current end of the urgent data,
; relative to the current send sequence.


;TCB/	Pointer to connection block
;			NOINT
;	CALL SETRUP
;Ret+1:	Always.  TSURP setup and TSURG turned on.

SETURP:	PUSH P,BFR		; Need this global for scanning buffers
	TEMP <CNT,NXT>		; Give names to T1, T2
	MOVEI CNT,0		; Assume no partial buffer
	LOAD BFR,TSCB,(TCB)	; Get partial buffer if any
	JUMPE BFR,SETUR1	; Jump if none
	SETSEC BFR,INTSEC	; Make extended address
	LOAD CNT,BCNT,(BFR)	; Get number of unsent bytes from bfr
SETUR1:
	MOVEI NXT,TCBSBQ(BFR)	; Pointer to send buffer queue head
SETUR2:	MOVE BFR,NXT		; Point bfr to what we will process
	CAIN BFR,TCBSBQ(BFR)	; Back to the queue head
	  JRST SETUR3		; Means done.  Go finish up.
	SETSEC BFR,INTSEC	; Make extended address
	LOAD NXT,QNEXT,+TCBSBQ(TCB) ; Get pointer to next item for next time
	LOAD T3,BCNT,(BFR)	; Get count from this buffer
	ADD CNT,T3		; Add into total
	JRST SETUR2		; Loop over entire queue, incl. bfr being sent
SETUR3:
	LOAD T3,TSSEQ,(TCB)	; Next send seq. num. to be used
	ADD T1,T3		; Compute 1st non-urgent seq. num.
	MODSEQ T1		; Keep within the right number of bits
	STOR CNT,TSURP,(TCB)	; Set the urgent pointer into the TCB
	SETONE TSURG,(TCB)	; Say we are in send urgent mode
	POP P,BFR
	RESTORE
	RET

; .RECV		Receive a buffer

;T1/	Flags,,JCN (or pointer to CDB)
;T2/	Pointer to buffer header
;
;	RECV
;Ret+1:	 Error.  Code in T1
;Ret+2:	Success

.RECV::	MCENT			; Enter monitor context
	TXNE T1,<STSFLG!TCP%FS!TCP%PS!TCP%VT!TCP%HP!TCP%SC>&↑-<TCP%JS!TCP%WT>
	  JRST TCPILP		; Illegal control bit
	XMOVEI T1,RECV1		; Routine to call via CHKARG
	CALL CHKARG		; Check arguments, set TCB, call RECV1
	JUMPL T1,TCPERR		; Error.
	UMOVE T1,T1		; Get flags
	TLNN T1,(TCP%WT)	; Supposed to wait?
	  SMRETN		; No.  Give immediate skip return
RECVW:	LOAD T1,BIDX,(BFR)
	LOAD T2,TERRF,(TCB)	; Error Flag index
	ROT T2,-<WID(TERRF)>	; Put in high bits of T2
	LSHC T1,↑D18+<WID(TERRF)>	; Put indexes in LH
	HRRI T1,INTOOT		; Select RECV done test routine
	MDISMS
	JN TERR,(TCB),TCPBER	; Jump if any error posted
	JRST TCPSKP

; RECV1(TCB)		Second phase of RECV

;T1/	JCN specified by caller
;TCB/	(Extended) Locked connection block
;			NOINT
;	CALL RECV1
;Ret+1:	Always.  T1 has 0 and BFR has the buffer, or T1 has-1,,error

RECV1:	JN TTVT,(TCB),RECV8	; Not allow for TVTs
;	LOCAL <OLDWND>
	LOAD T3,TRSYN,(TCB)	; Get receive state
	CAIE T3,NOTSYN		; Not synchronized
	 CAIN T3,FINRCV		; or FIN received?
	  JRST RECV9		; Yes.  Fail. (error code into buffer?)
	SETZ T2,		; Not allow options from CDB here
	CALL ACTTCB		; Try to activate the TCB (JCN in T1)
	JUMPL T1,RECV9		; Could not.
	CALL MAKBFR		; Make a buffer descriptor
	SKIPGE BFR,T1		; Check for error
	  EXIT RECVX		; There was one.
	LOAD T1,TRBS,(TCB)	; Current amount of receive buffer space
	LOAD T2,BICNT,(BFR)	; How much more is being made available
	ADD T1,T2
	STOR T1,TRBS,(TCB)	; New amount (for window setting)
	MOVE T1,BFR		; Item to enqueue
	XMOVEI T2,TCBRBQ(TCB)	; Receive buffer queue head
	CALL NQ			; Enqueue this buffer there

;	LOAD OLDWND,TRWND,(TCB)	; Get the current (old) window
	CALL NUWNDO		; Setup the new window, maybe ENCPKT
;	STOR T1,TRWND,(TCB)	; Set into the TCB
;	JUMPN OLDWND,RECV4	; No ACK needed if not opening from 0
;	JUMPE T1,RECV4		; Must be actually opening to non-0
;	CALL FRCPKT		; Make PZ send an ACK
;RECV4:
	JN TRPP,(TCB),RECV5	; Jump if partially process pkt waiting
	LOAD T1,QNEXT,<+TCBRPQ(TCB)>	; Ptr to 1st thing on RA queue
	CAIN T1,TCBRPQ(TCB)	; Empty queue?
	  JRST RECV6		; Yes.  No use running RA
RECV5:

	JN TRCB,(TCB),RECV6	; No signal if RA already has a BFR
	LOAD T3,QNEXT,<+TCBRBQ(TCB)> ; Get next buffer on the queue
	SETSEC T3,INTSEC	; Make extended address
	CAME T3,BFR		; Will this new buffer restart RA?
	  JRST RECV6		; No.  No need to run RA
	$SIGNL(RA,0)		; Make Reassembler run now
RECV6:

REPEAT 0,<
	MOVE T1,TCPRA0		; Time to wait
	LOAD T2,TSSYN,(TCB)	; Get send state
	LOAD T3,TRSYN,(TCB)	; Get receive side state
	CAIE T2,SYNSNT		; SYN sent?
	 CAIN T2,SYNCED		; or Synchronized?
	  CAIN T3,SYNABL	; And have heard something from other end?
	   CAIA
	    CALL ENCPKT		; Announce the new window in (T1) time
> ; End of REPEAT 0

	TDZA T1,T1		; Say OK to caller
RECV9:	  HRROI T1,ELP+↑D12	; "Connection Closing"
RECVX:;	RESTORE
	RET

RECV8:	HRROI T1,ELP+↑D30	; Only internet fork can run TVTs
	RET

; .OPEN		Open a connection

;T1/	Flags,,Pointer to Connection Descriptor Block (CDB)
;T2/	Persistence, seconds (max is TCPPTM)
;T3/	RX parameters
;
;	OPEN
;Ret+1:	 Error.  T1 has <JCN,,code>.	ELP+↑D1 - bad bit (TCP%JS)
;Ret+2:	Success.

.OPEN::	MCENT			; Enter the monitor context
	TXNE T1,<STSFLG!TCP%JS>&↑-<TCP%WT> ; JCN supplied is an error
	  JRST TCPILP		; Illegal control bit
	XMOVEI T1,OPEN1		; Routine to call via CHKARG
	CALL CHKARG		; Check arguments, set TCB, call OPEN1
	JUMPL T1,OPENE		; Jump if there was an error ?? JCN/TCB
	UMOVE T2,T1		; Get flags
	TLNE T2,(TCP%WT)	; Supposed to wait?
	  JRST OPENW		; Yes.
OPENOK:	TLO T1,(TCP%JS)		; Turn on JCN Supplied bit for him
	UMOVEM T1,T1		; Give JCN to user
	SMRETN

OPENW:	PUSH P,T1		; Save the JCN
	LOAD T1,TOPNF,(TCB)	; Get ID of Open Flag for this TCB
	LOAD T2,TERRF,(TCB)	; Error Flag index
	ROT T2,-<WID(TERRF)>	; Put in high bits of T2
	LSHC T1,↑D18+<WID(TERRF)>	; Put indexes in LH
	HRRI T1,INTOOT		; Select OPEN Done Test
	MDISMS
	POP P,T1
	LOAD T2,TERR,(TCB)	; Get error code
	JUMPE T2,OPENOK		; Jump if no error
	HRLZS T1		; JCN left half
	HRR T1,T2		; Put error code in right half
	SKIPA
OPENE:	  HRRZS T1		; ?? JCN/TCB
; Ought to get rid of JCN & TCB since returning error
	JRST TCPERO

; OPEN1(TCB)		Second phase of OPEN JSYS

;T1/	JCN resulting from CDB specified by caller
;T2/	Option addresses word, or 0 if none specified
;TCB/	(Extended) Locked connection block
;			NOINT
;	CALL OPEN1
;Ret+1:	Always.  T1 has -1,,error or the JCN
;		-1,,ELP+↑D6	Already open
;		-1,,ELP+↑D12	Closing (one side or other NOTSYN)
;		-1,,ELP+↑D30	TCP%VT not allowed by user jobs


OPEN1:	LOCAL <USRAC1,JCN,UOPTS>
	MOVEM T1,JCN
	MOVEM T2,UOPTS
	UMOVE USRAC1,T1		; Get the flags

	TLNN USRAC1,(TCP%VT)	; Virtual terminal?
	  JRST OPEN1A		; Not a virtual terminal
	HRROI T1,ELP+↑D30	; "Only Internet fork can run TVTs"
	MOVE T2,FORKX		; Which fork this is
	CAME T2,INTFRK		; The Internet fork?
	  JRST OPENX		; No.  Give error return  ?? JCN/TCB
OPEN1A:
	JN TSUOP,(TCB),OPEN6	; Jump if already open  ??? error or not???
				;		?? JCN/TCB
IFN MNET,<			; This code only if support multiple nets
	LOAD T1,TFH,(TCB)	; Get foreign host
	JUMPE T1,OPEN1D
	PUSH P,P1		; Save AC
	CALL FNDNCT		; Get the NCT for that net
	 JRST [ POP P,P1	; Restore AC
		MOVE T1,DEFADR	; Use default address
		JRST OPEN1B]	; Join below
	MOVE T1,NTLADR(P1)	; get our address on that network
	POP P,P1		; Restore AC
OPEN1B:>
IFE MNET,<MOVE T1,INETID>	; If only one name get it
	STOR T1,TLH,(TCB)	; And stick it in the TCB
OPEN1D:
	MOVE T1,JCN
	MOVE T2,UOPTS
	CALL ACTTCB		; Try to activate the TCB 
	JUMPL T1,OPENX2		; Cannot
	SETONE TSUOP,(TCB)	; Mark the TCB as open

	JE TNUFM,(TCB),OPEN5	; Skip following if old format
	HRRZ T1,USRAC1		; Connection block address
	UMOVE T1,.TCPIP(T1)	; Get IP parameter word
	STOR T1,TTOS,(TCB)	; Save type of service
	HLRS T1
	TRNE T1,.RTJST(-1,PITTL) ; User specify non-zero time?
	  STOR T1,TTTL,(TCB)	; Yes, Save Time to live
	LSH T1,↑D<-18+2>	; Top two bits
	STOR T1,TIFDF,(TCB)	; Don't fragment
OPEN5:
	UMOVE T2,T2		; Get the send timeout from user
	JUMPE T2,OPEN4		; Don't change if no specification
	CAMLE T2,TCPPTM		; Be sure it is reasonable for add to TODCLK
	  MOVE T2,TCPPTM
	IMULI T2,↑D1000		; Make into milliseconds
	STOR T2,TSTO,(TCB)	; Set the new value into the TCB
OPEN4:

	UMOVE T1,T3		; Get Retrans. parameter word
	CALL RXPARS		; Change them in TCB

	TLNN USRAC1,(TCP%VT)	; Openning as a virtual terminal?
	  JRST OPEN3		; No
	SETONE TTVT,(TCB)	; Yes.  Mark TCB as such
OPEN3:
	TLNE USRAC1,(TCP%FS)	; Supposed to force synchronization?
	  CALL FRCPKT		; Yes.  Packetizer will do that.
				;  Wait a sec??
; ??Why isn't TSPRS set BEFORE FRCPKT is called??
	TLNN USRAC1,(TCP%PS)	; Supposed to be persistent?
	  JRST OPEN2		; No.
	SETONE TSPRS,(TCB)	; Yes, mark the TCB as such.
OPEN2:
	MOVE T1,JCN		; Value to return
	EXIT OPENX

;OPEN12:SKIPA T1,[-1,,ELP+↑D12]	; "Connection closing"

;Returning an error is bad since connection is open & cannot return
;both error and JCN,  either abort & return error or skip & return JCN

OPEN6:	  HRROI T1,ELP+↑D6	; "Connection already open"
OPENX2:				; Probably bad options
OPENX:	RESTORE
	RET

; .CLOSE		Close a connection

;T1/	Flags,,JCN (NOTE: don't allow CDB here since it would create a TCB)
;
;	CLOSE
;Ret+1:	 Error, Code in T1
;		ELP+↑D1   Bad JCN, No TCB, CDB not allowed
;		ELP+↑D3   Was never open
;Ret+2:	Success

.CLOSE::MCENT			; Enter the monitor context
	TXNE T1,TCP%JS		; JCN must be supplied
	 TXNE T1,<STSFLG!TCP%FS!TCP%PS!TCP%VT!TCP%HP!TCP%SC>&↑-<TCP%JS!TCP%WT>
	  JRST TCPILP		; Illegal control bit
	HRRZS T1		; Save just the JCN part
	XMOVEI T2,CLOSE1	; Select CLOSE1 routine
	CALL CHKJCN		; Check access, set TCB, call CLOSE1
	JUMPL T1,TCPERR		; Jump if error.
	UMOVE T1,T1		; Get flags
	TLNN T1,(TCP%WT)	; Supposed to wait?
	  JRST CLOSEX		; No.  User will do ABORT to release JCN
	LOAD T1,TOPNF,(TCB)	; Get ID of Open Flag for this TCB
	LOAD T2,TERRF,(TCB)	; Error Flag index
	ROT T2,-<WID(TERRF)>	; Put in high bits of T2
	LSHC T1,↑D18+<WID(TERRF)> ; Put indexes in LH
	HRRI T1,INTZOT		; Select Close Done Test
	MDISMS
	LOAD T1,TERR,(TCB)	; Get the error code
	JUMPN T1,TCPERR		; Jump if error code non-null
	LOAD T1,TJCN,(TCB)	; Get the JCN for this connection
	CALL RETJCN		; Release it
CLOSEX:	SMRETN

; CLOSE1(TCB)		Second phase of CLOSE JSYS

;T1/	JCN Specified by caller (ignored here)
;TCB/	(Extended) Locked Connection Block
;			NOINT
;	CALL CLOSE1
;Ret+1:	Always. T1 has 0 for OK, or -1,,error
;			ELP+↑D3 Connection not open


CLOSE1::JE TSOPN,(TCB),CLOSE3	; Was it ever open?
	JE TSUOP,(TCB),CLOSE3	; Still Open?
	SETZRO TSUOP,(TCB)	; No longer
	CALL FRCPKT		; Get a FIN sent by Packetizer
	TDZA T1,T1		; Tell caller OK
CLOSE3:	  HRROI T1,ELP+↑D3	; "Connection not open"
	RET
;	RESET%
;	  V
;	CLZFF%
;	  V
;	ABTJCS
;	  V
;	ABTJC1	ABORT%
; For all JCN	  V
;	ABTJCN	ABORT1
;	  V	  V
;	ABTTCB,RETJCN

; .ABORT		Abandon this end of a connection

;T1/	Flags,,JCN
;
;	ABORT
;Ret+1:	Error.  T1 has code.	ELP+↑D1 - CDB supplied
;Ret+2:	Success.  Nothing more will be heard about this connection.

.ABORT::MCENT			; Enter monitor context
	TXNE T1,TCP%JS		; JCN must be supplied
	 TXNE T1,<STSFLG!TCP%FS!TCP%PS!TCP%VT!TCP%HP!TCP%SC>&↑-<TCP%JS!TCP%WT>
	  JRST TCPILP		; Illegal control bit
	HRRZS T1		; Save just the JCN
	XMOVEI T2,ABORT1	; Select the routine to run
	CALL CHKJCN		; Check arguement, set TCB, run ABORT1
	JUMPL T1,TCPERR		; Jump if some sort of error
	MOVEI T1,TCPABT		; Select wait routine
	HRL T1,FORKX		; For this fork
	MDISMS
	SMRETN



; ABORT1(TCB)		Second phase of ABORT JSYS

;T1/	JCN specified by caller (ignored here)
;TCB/	(Extended) Locked Connection Block
;			NOINT
;	CALL ABORT1
;Ret+1:	Always.  T1 has 0 for passing to caller.

ABORT1:
	CALL ABTTCB		; Abort the connection and increment
				; # being aborted by this forkx
	LOAD T1,TJCN,(TCB)	; Get user's handle
	CALL RETJCN		; Release that.
	MOVX T1,OK		; Say OK to caller
	RET

; ABTJCS			Abort JCNs for forks (part of CLZFF & RESET)

;T1/	Job fork number of fork being considered
;
;	CALL ABTJCS
;Ret+1:	Always.

ABTJCS::SKIPE TCPON		; TCP enabled?
	 SKIPL TCPIFG		; TCP Initialized yet (JOB-0 startup)
	  RET			; No.
	SAVET			; CLZFF code requires this
	MOVE T3,T1		; Put in place for call via LCKCAL
	XMOVEI T1,TCBHLK	; Stabilize JCNTCB table in JSB
	XMOVEI T2,ABTJC1	; and call function to abort JCNs
	NOINT			; Retain control during this
	CALL LCKCAL
	MOVEI T1,TCPABT		; Wait for all to be aborted
	HRL T1,FORKX		; The ones by this fork, that is.
	MDISMS
	OKINT			; State is clean again
	RET


;T1/	Job fork number of fork being considered
;
; ABTJC1	Same as above, but called with TCBH Lock set, NOINT
; TCBHLK locked		NOINT

ABTJC1:	LOCAL <JCN,JOBFRK>
	PUSH P,TCB
	MOVEM T1,JOBFRK
	MOVSI JCN,-MAXJCN	; Set to scan table
ABTJC2:	HRRZ TCB,JCNTCB(JCN)	; Get pointer to TCB
	JUMPE TCB,ABTJC3	; Avoid non-pointers
	SETSEC TCB,INTSEC	; Make extended address
	XMOVEI T1,TCBLCK(TCB)	; Pointer to lock on that TCB
	XMOVEI T2,ABTJCN	; Function to abort a JCN
	MOVE T3,JOBFRK		; Argument for ABTJCN
	CALL LCKCAL		; Lock the TCB and Abort the JCN
ABTJC3:	AOBJN JCN,ABTJC2	; Loop over all
	POP P,TCB
	RESTORE
	RET

; ABTJCN(TCB)		; Abort a JCN (ie, the connection) (Part of CLZFF)

;T1/	Job fork number being considered
;TCB/	(Extended) Locked connection block
;TCBH/	Locked TCB Hash table
;			NOINT
;	CALL ABTJCN
;Ret+1:	Always.

ABTJCN:	LOAD T2,TOWNR,(TCB)	; Get job number of owner
	CAME T2,JOBNO		; Better be ours
	  TCPBUG(CHK,<ABTJCN: TCP Conn not owned by aborting job>,TCPJS4)
	LOAD T2,TOFRK,(TCB)	; Get job fork handle of owning fork

	UMOVE T3,T1		; Get CLZFF flags from caller
	CAME T1,T2		; Was JCN created by the object fork?
	  JRST ABTJC4		; No.
	TXNN T3,CZ%NSF		; Yes. Are we supposed to abort there?
	  JRST ABTJC5		; Yes.  Go do it
	EXIT ABTJCX

ABTJC4:	EXCH T1,T2		; Get to right places for SKIIFA
	TXNN T3,CZ%NIF		; Abort inferiors' connections?
	 CALL SKIIFA		; Check owner inferior to object fork
	  EXIT ABTJCX		; Should not kill it
ABTJC5:
	NOSKED
; ??Why not CALL ABORT1 for these?
	CALL ABTTCB		; Get the TCP fork to do the work
	LOAD T1,TJCN,(TCB)	; Get the JCN
	CALL RETJCN		; Release that

	OKSKED
ABTJCX:	RET

; ABTTCB(TCB)			Get the TCB aborted (by PZ or CLZFF
;						or by ABORT)

;TCB/	Locked Connection Block
;			NOINT, maybe NOSKED
;	CALL ABTTCB
;Ret+1:	Always.

ABTTCB::NOSKED
	JN TSABT,(TCB),ABTTCX	; Already being aborted?
	SETONE TSABT,(TCB)	; No.  Make it so.
	SETZRO TSUOP,(TCB)	; Fake a CLOSE
	MOVE T1,FORKX		; Our fork number
	STOR T1,TABTFX,(TCB)	; Indicate which is killing the TCB
IFKA <	CALL ABTPTR>		; Simulate ADJBP ...
IFNKA <	ADJBP T1,FKABCP>	; Pointer to base of counters
	LDB T2,T1
	CAIGE T2,<1←ABTCBS>-1	; Do not allow count to wrap around
	  ADDI T2,1		; Bump the number killed by this fork
	DPB T2,T1
	$SIGNL(PZ,0)		; Run packetizer
ABTTCX:	OKSKED			; Note: new macro should not require this
	RET

	RESCD
; TCPABT(FORKX)			Scheduler test for ABORT(s) done

;T1/	a FORKX
;T4/	Return address
;
;	JSP T4,TCPABT
;Ret+1:	 One or more connections still being aborted
;Ret+2:	All ABORTs completed


TCPABT::IFKA <CALL ABTPTR>	; Simulate ADJBP ...
	IFNKA <ADJBP T1,FKABCP>
	LDB T2,T1
	JUMPE T2,1(T4)
	JRST 0(T4)


IFKA <
; ABTPTR(Number)	Get byte ptr to Nth abort counter

;T1/	The counter index (a FORKX)
;
;	CALL ABTPTR
;Ret+1:	Always.  Pointer in T1.

ABTPTR::PUSH P,T2		; Save an AC
	IDIVI T1,<↑D36/ABTCBS>	; Divide by number of bytes/word
	ADD T1,ABTTAB(T2)	; Add word offset to pointer
	POP P,T2
	RET

	XX==ABTCBS
ABTTAB:	REPEAT <↑D36/ABTCBS>,<	POINT ABTCBS,TCPABC,<-1+XX>
				XX==XX+ABTCBS
			      >
     >
	SWAPCD

; .STAT		Get status of a connection or the TCP

;T1/	Flags,,JCN or Pointer to CDB
;T2/	-N,,Offset	Number and beginning to return
;T3/	-M,,Address	Size and location in user space for results
;
;	STAT
;Ret+1:	 Error.  Code in T1
;			ELP+↑D20
;			ELP+↑D21
;		from CHKARG
;Ret+2:	Success

.STAT::	MCENT			; Enter monitor context
	TXNE T1,<TCP%FS!TCP%PS!TCP%VT!TCP%HP!TCP%SC>&↑-<TCP%JS!TCP%WT!STSFLG>
	  JRST TCPILP		; Illegal control bit
	TXNE T1,TCP%ST		; Asking for TCP statistics?
	  JRST STATS		; Yes
	TXNE T1,TCP%NT		; AOBJN pointer for TVTs wanted?
	  JRST STATNT		; Yes
	TXNE T1,TCP%NI		; AOBJN pointer for connections wanted?
	  JRST STATNI		; Yes
	XMOVEI T1,STAT1		; Select routine to call
	CALL CHKARG		; Check arguments, set TCB, call STAT1
	JUMPL T1,TCPERR		; There was something wrong.
	SMRETN



; Return in 2/ -#TVTs,,first TVT

STATNT:	MOVE T2,TVTPTR		; Get AOBJN pointer
	UMOVEM T2,2		; to user
	SMRETN			; All ok



; Return in 2/ -# connections,,1

STATNI:	MOVN T2,TCBCNT		; # connections
	HRLS T2			; in LH
	HRRI T2,1		; First connection #
	UMOVEM T2,2		; to user
	SMRETN			; All ok

; Just copy the statistics area to user space

STATS:	SETZ TCB,		; Be safe
	TXNE T1,TCP%SY		; Giving symbolic names?
	  JRST STATS9		; Yes
	HLRE T1,T2		; Get count
	MOVNS T1		; As a positive number
	HLRE T4,T3		; Get size of user's area
	MOVNS T4		; As a positive number
	CAMLE T1,T4		; Take min as size of transfer
	  MOVE T1,T4
	MOVEI T4,0(T2)		; Start point
	ADD T4,T1		; End + 1
	CAILE T4,STATZZ-STAT0	; Compare with size of statistics area
	  JRST STATS8		; Tell him it is bad.
	PUSH P,T1		; Save for awhile
	MOVEI T2,STAT0(T2)	; Start address within statistics area
	HRRZS T3		; Assume user section 0
	CALL BLTMU		; Transfer from monitor to user
	POP P,T4		; Recover size
	HRLS T4			; Make N,,N
	XCTU [ADDM T4,T2]	; Update user's pointers
	XCTU [ADDM T4,T3]
	SMRETN

STATS8:	HRROI T1,ELP+↑D21	; Bad arg to STAT
	JRST TCPERR

STATS9:	CALL STATNM		; Do work
	JUMPL T1,TCPERR		; Error exit
	SMRETN

; STAT1(TCB)			Second phase of STAT JSYS

;T1/	JCN specified by caller (ignored here)
;TCB/	(Extended) Locked connection block
;			NOINT
;	CALL STAT1
;Ret+1:	Always.  T1 has 0 for OK, or -1,,error
;			-1,,ELP+↑D20
;			-1,,ELP+↑D21

STAT1:	LOCAL <XFRCNT>
	UMOVE T1,T1		; Get flags
	UMOVE T2,T2		; Get pointer
	UMOVE T3,T3		; Get pointer to user space
	TXNE T1,TCP%SY		; Giving symbolic names?
	  JRST STAT6		; Yes

	JUMPGE T2,STAT9		; Strange pointer
	JUMPGE T3,STAT9		; Strange pointer
	HLRE T1,T2		; Get count
	MOVNS T1		; As a postive number
	HLRE XFRCNT,T3		; Get size of user's area
	MOVNS XFRCNT		; As a postive number
	CAMLE XFRCNT,T1		; Take min as size of transfer
	  MOVE XFRCNT,T1
	HRRZ T4,T2		; Start offset
	CAIL T4,TCBSIZ		; Must be within TCB
	  JRST STAT8		; Tell him "bad arg"
	ADD T4,XFRCNT		; Compute end+1
	CAILE T4,TCBSIZ		; Trying to read too much?
	  JRST STAT8		; Tell him arg is bad.
	HRRZS T2		; Flush the count
	ADD T2,TCB		; Start address within TCB
	HRRZS T3		; Flush the count (assume user sec 0)
	MOVE T1,XFRCNT		; Set up count
	CALL BLTMU		; Transfer from monitor to user
	HRLS XFRCNT
	XCTU [ADDM XFRCNT,T2]	; Update user's pointers
	XCTU [ADDM XFRCNT,T3]
	MOVX T1,OK		; Tell caller all is well
	EXIT STATX

STAT6:	CALL STATNM		; Do the work
	JRST STATX

STAT8:	SKIPA T1,[-1,,ELP+↑D20]	; "Funny pointer to STAT"
STAT9:	  HRROI T1,ELP+↑D21	; "Bad transfer size to STAT"
STATX:	RESTORE
	RET
; Symbolic Routines

; T1/	User flags
; T2/	Input count/pointer
; T3/	Output count/pointer
;	CALL STATNM
;Ret+1:	Always  T1 has error code or 0

STATNM:	LOCAL <UFL,INP,OUP>
	PUSH P,TCB-1		; Used for STAT0
	XMOVEI TCB-1,STAT0	; References
	JUMPGE T2,STATNV	; IN pointer error
	JUMPGE T3,STATNV	; OUT pointer error
	MOVEM T1,UFL		; Save flags (TCP%SD)
	MOVEM T2,INP		; Save pointers
	MOVEM T3,OUP

; Know have valid input ptr & at least 1 output slot

STATN3:	UMOVE T4,(INP)		; Get name
	CALL SRCH		; Lookup name
	JUMPE T2,STATNW		; Lose
	TXNE UFL,TCP%SD		; Want pointer or value?	
	  MOVEI T2,1		; Pointer has only one value

	TXNE UFL,TCP%SD		; Want pointer or value?
	 SKIPA T1,T3		; Get pointer
	  LDB T1,T3		; Get value

STATN7:	UMOVEM T1,(OUP)		; For user
	SOS T2			; One less to go
	AOBJP OUP,STATNU	; Leave if output full
	JUMPLE T2,STATN8	; End Multiple
	ILDB T1,T3		; Get value
	JRST STATN7

STATN8:	AOBJN INP,STATN3	; More input?
	SETZ T1,		; No, All done w/o error
	JRST STATNX

STATNU:	SKIPN T1,T2		; Error if more to output
	  AOBJP INP,STATNX	; Or more input
STATNV:	SKIPA T1,[-1,,ELP+↑D21]	; Bad pointers
STATNW:	  HRROI T1,ELP+↑D22	; Invalid name

STATNX:	UMOVEM INP,2		; Return updated input
	UMOVEM OUP,3		; And output pointers
	POP P,TCB-1		; Restore register
	RESTORE
	RET			; Return

; Exact Match Binary Search Routine

; T4/	Symbol
;	CALL SRCH
; T3/	Pointer
; T2/	Count

SRCH:	TEMP <PRB,XXX,OFS,KEY>
	SETZB PRB,T2		; Offset into table & Assume missing
	MOVX OFS,1←<↑D<36-↑L<STABLN>>> ; Get Initial offset (next 2**N)
SRCHF:	ADD PRB,OFS		; Move forward (double)
SRCHR:	LSH OFS,-1		; Next time
	  SUB PRB,OFS		; Move reverse
	JUMPLE OFS,SRCHX	; Stop if no move
	CAIG PRB,STABLN		; Point too far? or
	 CAMGE KEY,STSTAB(PRB)	; Value too big?
	  JRST SRCHR		; Yes, move back
	CAML KEY,STSTAB+1(PRB)	; As far as next?
	  JRST SRCHF		; Yes, move forward
SRCHX:	CAME KEY,STSTAB(PRB)	; Exact match?
	  RET			; No, error (T2 is 0)
	MOVE T3,STATPT(PRB)	; Value
	MOVE T2,STATCT(PRB)	; Count
	RESTORE
	RET


; Symbolic STAT tables

DEFINE DEFSTS <
	XX (M,ACDLAY,HISTSZ)
	XX (M,BGRNCT)
	XX (M,BGUSE)
	XX (M,BYTRCT)
	XX (M,BYTSCT)
	XX (M,DGRNCT)
	XX (M,DGUSE)
	XX (M,DUPKCT)
	XX (M,FINRCT)
	XX (M,FINSCT)
	XX (M,INTBYP)
	XX (M,IPDLAY,HISTSZ)
	XX (M,IPPKCT)
	XX (M,IPRNCT)
	XX (M,IPUSE)
	XX (M,OHUSE)
	XX (M,OPDLAY,HISTSZ)
	XX (M,OPPKCT)
	XX (M,OPRNCT)
	XX (M,OPUSE)
	XX (M,PZDLAY,HISTSZ)
	XX (M,PZPKCT)
	XX (M,PZRNCT)
	XX (M,PZUSE)
	XX (M,RADLAY,HISTSZ)
	XX (M,RAPKCT)
	XX (M,RARNCT)
	XX (M,RAUSE)
	XX (M,RSTRCT)
	XX (M,RSTSCT)
	XX (M,RXDLAY,HISTSZ)
	XX (M,RXPKCT)
	XX (M,RXRNCT)
	XX (M,RXUSE)
	XX (M,SYNRCT)
	XX (M,SYNSCT)
	XX (T,TABTFX)
	XX (M,TASKCT)
	XX (T,TCBIO,<1←<WID(PIDO)>-1-<MINIHS+3>/4>)
	XX (T,TCBIR,<1←<WID(PIDO)>-1-<MINIHS+3>/4>)
	XX (T,TCBIU,<1←<WID(PIDO)>-1-<MINIHS+3>/4>)
	XX (T,TCBTO,<1←<WID(PTDO)>-1-<MINTHS+3>/4>)
	XX (T,TCBTR,<1←<WID(PTDO)>-1-<MINTHS+3>/4>)
	XX (T,TCBTU,<1←<WID(PTDO)>-1-<MINTHS+3>/4>)
	XX (T,TCTBS)
	XX (T,TCTSQ)
	XX (T,TERBF)
	XX (T,TERJN)
	XX (T,TERR)
	XX (T,TERRF)
	XX (T,TERRT)
	XX (T,TFH)
	XX (T,TFP)
	XX (T,TIFDF)
	XX (T,TIPDO)
	XX (T,TIPOR)
	XX (T,TIPOU)
	XX (T,TJCN)
	XX (T,TLH)
	XX (T,TLP)
	XX (T,TMNRT)
	XX (T,TMXRT)
	XX (T,TOFRK)
	XX (T,TOPFH)
	XX (T,TOPFP)
	XX (T,TOPLH)
	XX (T,TOPNF)
	XX (T,TOWNR)
	XX (T,TPICA)
	XX (T,TPICE)
	XX (T,TPICR)
	XX (T,TPICS)
	XX (T,TPICU)
	XX (T,TPICX)
	XX (T,TPIFA)
	XX (T,TPIFE)
	XX (T,TPIFR)
	XX (T,TPIFS)
	XX (T,TPIFU)
	XX (T,TPIFX)
	XX (T,TRBS)
;	XX (T,TRCB)
	XX (T,TRCBY)
	XX (T,TRIS)
	XX (T,TRLAK)
	XX (T,TRLFT)
	XX (T,TRLWN)
;	XX (T,TRPB)
	XX (T,TRPP)
	XX (T,TRSYN)
	XX (T,TRURG)
	XX (T,TRURP)
	XX (T,TRWND)
	XX (T,TRXI)
	XX (T,TRXPD)
	XX (T,TRXPI)
	XX (T,TRXPN)
	XX (T,TSABT)
;	XX (T,TSAP)
	XX (T,TSBYT)
	XX (T,TSCB)
	XX (T,TSCR)
	XX (T,TSEP)
	XX (T,TSFP)
	XX (T,TSLFT)
	XX (T,TSLVC)
	XX (T,TSLVN)
	XX (T,TSMRT)
	XX (T,TSMXB)
	XX (T,TSMXP)
	XX (T,TSOPN)
	XX (T,TSPRS)
	XX (T,TSSEQ)
	XX (T,TSSV)
	XX (T,TSSYN)
	XX (T,TSTO)
	XX (T,TSUOP)
	XX (T,TSURG)
	XX (T,TSURP)
	XX (T,TSWND)
	XX (T,TTOS)
	XX (T,TTPDO)
	XX (T,TTPOR)
	XX (T,TTPOU)
	XX (T,TTTL)
	XX (T,TTVT)
	XX (T,TVTL)
	XX (T,TWLDN)
	XX (T,TWLDP)
	XX (T,TWLDT)
> ; End of DEFINE DEFSTS
; Construct the ASCII Name Table

DEFINE XX (TYP,NAM,LEN)<
IFLE <ASCII /NAM/>-..XL,<PRINTX ? DEFSTS NAM is truncated or out of order>
	..XL=ASCII /NAM/
	EXP ..XL
> ; End of DEFINE XX


	..XL=400000000000
STSTAB:	400000000000		; Minimum
;	DEFSTS			; Status names
	XLIST
	DEFSTS			; Status names
	LIST
	377777777777		; Maximum
STABLN=.-STSTAB-2


; Construct the Count Table

DEFINE XX (TYP,NAM,LEN)<
	IFB  <LEN>,<1>
	IFNB <LEN>,<LEN>
> ; End of DEFINE XX

STATCT:	0			; Minimum
;	DEFSTS			; Status counts
	XLIST
	DEFSTS			; Status counts
	LIST
	0			; Maximum


; Construct the LDB Pointer Table

DEFINE XLDB  (L,O,M)<	<↑D<35-POS(M)>>B5+<WID(M)>B11+<TCB>B17+O >

DEFINE XX (TYP,NAM,LEN)<
	..XL=-1
  IFIDN <TYP><M>,<			POINT 36,NAM-STAT0(TCB-1),35
		..XL=..XL+1> ; End IFIDN M
  IFIDN <TYP><T>,<	IFNDEF %'NAM,<	POINT 36,NAM(TCB),35>
			IFDEF  %'NAM,<	%'NAM (XLDB,,,NAM)>
		..XL=..XL+1> ; End IFIDN T
  IFN ..XL,<PRINTX ? Type code for NAM must be M or T>
> ; End of DEFINE XX


STATPT:	0
;	DEFSTS			; Status pointers
	XLIST
	DEFSTS			; Status pointers
	LIST
	0

	PURGE ..XL

; .CHANL		Set TCP event interrupt channels

;T1/	Flags,,JCN (or pointer to CDB)
;T2/	Six 6-bit bytes (channel numbers)
;	77 - No change, or 0-5, 24-35 Channel to get intertupt
;	CHANL
;Ret+1:	 Error, Code in T1.
;		from CHKARG
;			ELP+↑D17 Bad arg to CHANL
;Ret+2:	Success

.CHANL::MCENT			; Enter monitor context
	TXNE T1,<STSFLG!TCP%FS!TCP%PS!TCP%VT!TCP%HP!TCP%SC>&↑-<TCP%JS!TCP%WT>
	  JRST TCPILP		; Illegal control bit
	XMOVEI T1,CHANL1	; Select routine to call via CHKARG
	CALL CHKARG		; Check arguments, set TCB, call CHANL1
	JUMPL T1,TCPERR		; Jump if something is wrong.
	SMRETN

; CHANL1(TCB)			Second phase of CHANL JSYS

;T1/	JCN specified by caller (ignored here)
;TCB/	(Extended) Locked Connection Block
;			NOINT
;	CALL CHANL1
;Ret+1:	Always.  T1 has 0 if OK, or -1,,error
;			-1,,ELP+↑D17 Bad arg to CHANL

CHANL1:	TEMP <NEW,OLD,CNT,FORKID>
	LOCAL <NEWCHS,NEWPTR,OLDPTR,FRKPTR>
	UMOVE NEWCHS,T2		; Get channel word from user
	MOVE NEWPTR,[POINT 6,NEWCHS]	; Set to scan them
	MOVE OLDPTR,[POINT 6,TCBPIC(TCB)]; Set to scan current ones
	MOVE FRKPTR,[POINT 18,TCBPIF(TCB)]; Set to scan forks
	MOVEI CNT,6		; How many to scan
	MOVE FORKID,FORKX	; Who is setting the new channels

CHANL2:	ILDB NEW,NEWPTR		; Get a new setting
	ILDB OLD,OLDPTR		; and what was there before
	CAIE NEW,77		; No change mark?
	 CAIG NEW,5		; OK number for the channel?
	  JRST CHANL3		; Take the good number
	CAIL NEW,↑D24		; These are also OK
	 CAILE NEW,↑D35
	  JRST CHANL9		; Bad.  Tell user.
CHANL3:
	CAIE NEW,77		; No change?
	  MOVE OLD,NEW		; No.  New will replace old
	DPB OLD,NEWPTR		; Construct the replacement set
	IBP FRKPTR		; Move to current fork slot
	CAIE NEW,77		; Changing the channel
	  DPB FORKID,FRKPTR	; Yes.  This fork gets the PSIs now.
	SOJG CNT,CHANL2		; Loop over all six bytes

	MOVEM NEWCHS,TCBPIC(TCB); Stash into TCB
	TDZA T1,T1		; Tell caller all is well
CHANL9:	  HRROI T1,ELP+↑D17	; "Bad arg to CHANL"
	RESTORE
	RET

; .SCSLV	Set connection security level

;T1/	Flags,,JCN or pointer to CDB
;T2/	Security Level
;
;	SCSLV
;Ret+1:	 Error.  Code in T1
;		from CHKARG
;			ELP+↑D29 Security already set
;Ret+2:	Success.

.SCSLV::MCENT
	TXNE T1,<STSFLG!TCP%FS!TCP%PS!TCP%VT!TCP%HP!TCP%SC>&↑-<TCP%JS!TCP%WT>
	  JRST TCPILP		; Illegal control bit
	XMOVEI T1,SCSLV1	; Select routine to call via CHKARG
	CALL CHKARG		; Check args, set TCB, call SCSLV1
	JUMPL T1,TCPERR		; Give error return if appropriate
	SMRETN			; Otherwise, it was good.



; SCSLV1(TCB)		Second Phase of SCSLV JSYS

;T1/	JCN specified by caller (ignored here)
;TCB/	Locked connection block
;			NOINT
;	CALL SCSLV1
;Ret+1:	Always.  T1 has 0 if OK, or -1,,error
;			-1,,ELP+↑D29 Security already set

SCSLV1:	UMOVE T2,T2		; Get arg from caller
	JN TSLVN,(TCB),SCSLVE	; Bad.  No changes allowed.
	STOR T2,TSLVN,(TCB)	; Set the new value
	TDZA T1,T1		; Get a 0 to indicate OK
SCSLVE:	  HRROI T1,ELP+↑D29	; "Can't change security levels"
	RET

; TATNVT	Part of ATNVT JSYS for TVTs, Returns to USER w/ w/o skip
;		Attach a TVT to a User TCB; Called in non-Job-0 context

;T1/	Flags+JCN
;
;	JRST TATNVT
;Ret+1:	 Failed, Error code in T1, JCN still valid
;	ATNX1	-1,,ELP+↑D1	Invalid JCN
;	ATNX2			Receive side not SYNCED
;	ATNX3			User CLOSEd/ABORTed connection
;	ATNX5			Recieve side has been used (RECVs)
;	ATNX6			Connection has been closed, or has errors
;	ATNX8			Send side not SYNCED
;	ATNX11			Send side has been used (SENDs)
;	ATNX13	-1,,ELT+↑D4	No TVTs or
;		-1,,ELT+↑D31	TCP not Initialized
;Ret+2:	Success, T1 contains TTY designator for TVT
;		 JCN has been released

;	MCENT			; Already in Monitor context
TATNVT::XCTU [HRRZ T1,1]	; Get JCN w/o flags
	TXO T1,<TCP%JS>		; Set JCN Supplied
	UMOVEM T1,1		; Put it back for CHKARG

	XMOVEI T1,TATNV1	; Routine to call
	CALL CHKARG		; Check arg, set TCB, call TATNV1
	JUMPL T1,TATNV0		; Give error return

	LOAD T1,TVTL,(TCB)	; Make TTY descriptor
	TXO T1,<.TTDES>
	UMOVEM T1,1		; Return TT Descriptor
	SMRETN			; OK (skip) return



TATNV0:	HRRZS T1		; Drop -1,, for compares
	CAIN T1,<ELP+↑D1>	; Translate TCP error code into TOPS20
	  MOVX T1,<ATNX1>
	CAIE T1,<ELT+↑D4>
	CAIN T1,<ELT+↑D31>
	  MOVX T1,<ATNX13>
	JRST TCPERR		; Return error

; TATNV1 (TCB,JCN)	; Second phase of TATNVT

; T1/	JCN supplied by caller
; TCB/	Locked connection block
;			NOINT
;	CALL TATNV1
;Ret+1:	Always. T1 has -1,,error, or TTY descriptor otherwise


TATNV1:	LOCAL <JCN>
	MOVEM T1,JCN

	MOVX T1,<-1,,ATNX2>
	LOAD T2,TRSYN,(TCB)	; Receive side SYNCED?
	CAIE T2,SYNCED
	  JRST TATNV9		; No, error

	MOVX T1,<-1,,ATNX8>
	LOAD T2,TSSYN,(TCB)	; Send side SYNCED?
	CAIE T2,SYNCED
	  JRST TATNV9		; No, error

	MOVX T1,<-1,,ATNX5>
	LOAD BFR,QNEXT,<+TCBRBQ(TCB)>
	CAIE BFR,TCBRBQ(TCB)	; Without receive buffers
	  JRST TATNV9		; Has buffer, error

	MOVX T1,<-1,,ATNX11>
	LOAD BFR,QNEXT,<+TCBSBQ(TCB)>
	CAIE BFR,TCBSBQ(TCB)	; Without send buffers
	  JRST TATNV9		; Has buffer, error

	MOVX T1,<-1,,ATNX3>
	JE TSUOP,(TCB),TATNV9	; Not OPENed by user error
	MOVX T1,<-1,,ATNX6>
	JE TSOPN,(TCB),TATNV9	; Not still OPEN error
	JN TERR,(TCB),TATNV9	; Had some error error

	HRRZ T1,TCB		; ASNTVT wants TCB &
	TXO T1,AN%NTP		; Say it will speak new Telnet
	CALL ASNTVT		; Assign a virtual terminal
	  JRST TATNV8		; Failed (no TVT available, etc)
	STOR T1,TVTL,(TCB)	; Save TTY # connection block

; Forget everything about Job which opened connection & give to Job0

	MOVE T1,JCN		; Our JCN
	CALL RETJCN		; Release PSIs & JCN
	SETZRO TOWNR,(TCB)	; Transferred to Job0
	SETONE TJCN,(TCB)	; without a JCN (hard to get to Job0 JSB)

	SETONE TTVT,(TCB)	; Say its a TVT
; T2 from ASNTVT
	CALL ULKTTY		; Block now stable
	TDZA T1,T1		; OK
TATNV8:	  MOVX T1,<-1,,ATNX13>	; Out of resources error (TVTs)
TATNV9:	RESTORE
	RET

; ACTTCB(TCB)		Activate a connection

; ACTTCB tries to move a connection from the completely unsynchronized
; (closed or brand new) state into the SYNABLE state, where it is
; able to send and/or repond to SYNs.  Activating a connection is the
; operation performed by user calls like OPEN, SEND and RECV, and make
; the connection be "alive".  If the connection is already active, this
; results in a true value.  False is return if the connection is
; partially closed -- one side or the other is NOTSYN state.


;T1/	JCN
;T2/	Option addresses word from OPEN, or 0 if otherwise
;TCB/	(Extended) Locked connection block
;			NOINT
;	CALL ACTTCB
;Ret+1:	Always.  T1 has 0 if successfully activated, error code otherwise

; **** Preserve T2 until TCPUOP

ACTTCB:	LOAD T4,TSSYN,(TCB)	; Get send state
	LOAD T3,TRSYN,(TCB)	; Get recv state
	CAIE T4,NOTSYN		; Unsynchronized?
	  JRST ACTTC7		; No.
	CAIE T3,NOTSYN
	  JRST ACTTC8		; Return FALSE

; NOTSYN-NOTSYN
	STOR T1,TJCN,(TCB)	; Indicate this TCB is owned
	MOVE T3,TCB
	HRL T3,FORKX		; Form system fork,,TCB
	MOVEM T3,JCNTCB(T1)	; Store in job private table
; **** T2 Preserved
	SKIPE T1,T2		; Option address word
	 CALL TCPUOP		; Get options from user
	  JUMPL T1,ACTTCX	; Return error code ** RETJCN too

	MOVE T2,JOBNO		; Our job number
	STOR T2,TOWNR,(TCB)	; Store this as TCB Owner
	MOVX T1,SYNABL		; SYN Ok state
	STOR T1,TSSYN,(TCB)	; Set send side
	STOR T1,TRSYN,(TCB)	; and recv side
; Clear persistent SYN flag, Clear OPEN has been done flag
; Clear "said it's open" bit, Clear ABORT requested flag
; Clear TVT flag
	SETZRO <TSPRS,TSUOP,TSOPN,TSABT,TTVT>,(TCB)
	SETZRO TVTL,(TCB)	; Clear TVT line number
	SETZRO TSCPK,(TCB)	; No partially filled packet

	MOVE T1,INTXPB		; Maximum data size for a packet
	SUBI T1,MINIHS+MINTHS	; Assuming no options & largest net
; ?? Really want to jam that much at other end immediately??
	JFCL
;	STOR T1,TSWND,(TCB)	; Is the default initial send window
	JFCL
;	ASH T1,1		; Twice the maximum packet data size
	STOR T1,TRWND,(TCB)	; is the default initial receive window.
	SETZRO TRBS,(TCB)	; No RECV buffer space yet

	HRRZ T1,FORKN		; Our Job fork number
	STOR T1,TOFRK,(TCB)	; Say who owns the TCB

	SETO T1,
	STOR T1,TPSIC,(TCB)	; No PSI Channels named yet
	STOR T1,TPIFU,(TCB)	; No INTRP fork
	STOR T1,TPIFR,(TCB)	; No RECV DONE fork
	STOR T1,TPIFS,(TCB)	; No SEND DONE fork
	STOR T1,TPIFE,(TCB)	; No ERROR fork
	STOR T1,TPIFX,(TCB)	; No STATE CHANGE fork
	STOR T1,TPIFA,(TCB)	; No EOL ACK fork

	STOR T1,TRLWN,(TCB)	; No last window seq #

	MOVE T1,TCPRX0		; Good starting point for retrans
	STOR T1,TMNRT,(TCB)	; Minimum round trip time
	STOR T1,TMXRT,(TCB)	; Maximum round trip time
	STOR T1,TRXI,(TCB)	; Current RX interval
	SETZRO <TRXPN,TRXPD,TRXPI>,(TCB) ; Clear RX parameters

	MOVX T1,OK		; General success code
	STOR T1,TERR,(TCB)	; Indicate no error on this connection
	LOAD T1,TERRF,(TCB)	; Index of the error event flag
	CALL CLRWTB		; Clear it
	JRST ACTTC9		; Return true to say it is now active

ACTTC7:	CAIN T3,NOTSYN		; Check receive side state
ACTTC8:	 HRROI T1,ELP+↑D12	; "Connection closing" error
				; (S=NOTSYN, R.ne.NOTSYN or
				;  S.ne.NOTSYN, R=NOTSYN)
ACTTC9:	  SETZ T1,		; Return OK (S.ne.NOTSYN & R.ne.NOTSYN)
ACTTCX:	RET			; Return with TCPUOP's error code

; CHKARG(FUNC)		Check arg(s) of TCP call, set up TCB,
			; call FN(JCN,user option word or 0)
;T1/	(Extended) Function address
;T2/	ARG2 for FUNC	(***** obsolete *****)
;
;	CALL CHKARG
;Ret+1:	Always.  T1 has value of FUNC(JCN,ARG).  TCB has been setup.
;		Note:  TCB is locked & NOINT during call to FUNC
;	CHKJCN	-1,,ELP+↑D1	JCN out of range, or no TCB for JCN
;	GETJCN	-1,,ELT+↑D4	No free JCN, no space for TCB
;		-1,,ELT+↑D31	TCP not initialized
;	CHKADD	...
;	function ...


CHKARG:	STACKL <<ARGBLK,CHKADW>>
	CHKADL (USR)		; LOCAL
	XMOVEI PARAMS,ARGBLK	; Set the pointer
	MOVEM T1,FN		; Save function address
;	MOVEM T2,ARG1		; OBSOLETE
	SETZM ARG1

	NOINT
	SKIPE TCPON		; TCP turned on?
	 SKIPN TCPIFG		; TCP Initialized yet?
	  JRST CHKARI		; No.
	UMOVE T1,T1		; Get user's AC1 flags
	TXNE T1,TCP%IX		; Connection # specified?
	  JRST CHKAR3		; Yes
	TXNN T1,TCP%JS		; JCN Supplied in right half?
	  JRST CHKAR1		; No.  Go translate into one

; Given JCN

	HRRZS T1		; Save JCN part
	MOVE T2,FN		; Function to call if JCN ok
	MOVE T3,ARG1		; Argument to FN
	CALL CHKJCN		; Set TCB, Lock it & call FN
	EXIT CHKARX		; Return whatever result
; Given Connection block or TVT number

CHKAR1:	TXNE T1,TCP%TV		; TVT number specified?
	  JRST CHKAR2		; Yes

; Given Connection block
;	SETZM JCN		; No resources reserved
	CALL GETJCN		; Reserve a JCN
	JUMPL T1,CHKARX		; Couldn't.  Tell caller
	MOVEM T1,JCN		; Save the JCN

	XCTU [HRRZ USR,T1]	; Get ptr to Connection Descriptor Blk
	UMOVE T1,.TCPLH(USR)
	UMOVE T2,.TCPLP(USR)	; Copy the info from user area
	UMOVE T3,.TCPFH(USR)
	UMOVE T4,.TCPFP(USR)
	UMOVE USR,.TCPOP(USR)
; ****	Beginning of Compatability Kludge
	PUSH P,BHC+1		; Assume new format
	JUMPE T1,KLUDG0	; If first word 0, must be new (LP=0 illegal)
	TLNE T1,-1	; If first word is LP, then only rh 16 bits used
	  JRST KLUDG0		; New format
	MOVE T4,T3		; Map old format into new
	MOVE T3,T2
	MOVE T2,T1
	SETZB T1,USR		; New info zero if old format
	SETZM (P)		; Use old format
KLUDG0:
; ****	End of Compatability Kludge
	ANDX T1,.RTJST(-1,PISH)
	ANDX T2,.RTJST(-1,PSP)
	ANDX T3,.RTJST(-1,PIDH)
	ANDX T4,.RTJST(-1,PDP)
	MOVEM T1,LH
	MOVEM T2,LP		; Store into ARGBLK for CHKADD
	MOVEM T3,FH
	MOVEM T4,FP
	MOVEM USR,ARG1		; Option addresses is second arg for FN
	SETZM WILDOK		; Not OK to find listening connections
	MOVE T1,PARAMS		; Pointer to parameter block for CHKADD
	CALL CHKADD		; Find TCB, Lock it, Call FN
; ****	Beginning of Compatability Kludge
	POP P,T2		; Old (0)/New (1) flag
	JUMPL T1,CHKA19		; Jump if all went well
	STOR T2,TNUFM,(TCB)	; Save format flag
	JRST CHKARX
; ****	End of Compatability Kludge

CHKA19:	PUSH P,T1		; Save error result
	MOVE T1,JCN		; Get back the JCN
	CALL RETJCN		; To return & disown TCB ("DEAD")
	POP P,T1		; Restore error code
	EXIT CHKARX

; Given TVT #

CHKAR2:	MOVEI T2,(T1)		; TVT line # into 2

	CALL CHKTVT		; Check if valid TVT
	  JRST CHKART		; Lose

	CALL TVTCHK		; Get (locked) data base
	  JRST CHKARU		; Not fully active
	LOAD TCB,PTVT,(T2)	; Get TCB address
	CALL ULKTTY		; Unlock TTY data base

	JUMPE TCB,CHKART	; Illegal connection
	SETSEC TCB,INTSEC	; TCBs in this section
	MOVX T3,0 ;T1		; Unused Arg for FN is line type??
	XMOVEI T1,TCBLCK(TCB)	; Lock to lock
	MOVE T4,ARG1		; Second arg for FN
	MOVE T2,FN		; Function to call
	CALL LCKCAL
	JRST CHKARX		; Leave

; Find the nth connection specified by T1

CHKAR3:	HRRZS T1		; Just the number
	CAILE T1,0		; Must be greater than 0 and
	 CAMLE T1,TCBCNT	; Less than current number
	  JRST CHKART		; Lose, invalid index
	MOVEM T1,JCN		; Save index
	XMOVEI T1,TCBHLK	; Lock for TCB hash table
	CALL SETLCK		; Lock it
	PUSH P,TCB		; Save TCB

	MOVSI T2,-TCBHSZ	; Size of hash table
CHKA30:	HRRZ TCB,T2		; Current TCBH slot
	ADD TCB,TCBH		; Add base of table (including section)
	HRRZ T3,TCB		; Save head of list
CHKA31:	LOAD TCB,QNEXT,(TCB)	; Get next on list
	CAMN TCB,T3		; Back to head?
	  JRST [AOBJN T2,CHKA30	; Yes, jump back if another slot
		SETZ TCB,	; No more, TCB not found
		JRST CHKA32]	; Quit
	SETSEC TCB,INTSEC	; TCBs in this section
	SOSE JCN		; Count down index
	  JRST CHKA31		; Loop if not want this one

; TCB points to TCB or is 0

CHKA32:	AOS TCBHUC		; Bump hash table use count
	XMOVEI T1,TCBHLK	; TCBH lock
	CALL UNLCK		; Unlock it with non-zero count means reading
;	SETZM JCN		; No resource to release
	HRROI T1,<ELP+↑D1>	; Assume error
	SKIPN TCB		; Find a TCB?
	  JRST CHKA33		; No
	XMOVEI T1,TCBLCK(TCB)	; TCB to lock
	MOVE T2,FN		; Function to call
	MOVX T3,0 ;JCN		; Restore args (JCN=0 here)
	MOVE T4,ARG1
	CALL LCKCAL		; Call function
CHKA33:	SOS TCBHUC		; Done reading TCB
	POP P,TCB		; Restore register
	JRST CHKARX		; Leave, error code in T1


CHKARU:	CALL ULKTTY		; Maybe a non-standard block
CHKART:	HRROI T1,ELP+↑D1	; Illegal connection
	JRST CHKARX

CHKARI:	HRROI T1,ELT+↑D31	; "TCP Not initialized yet"

CHKARX:	OKINT
	CHKADR
	RET

; CHKJCN(JCN)			See if caller has access to JCN

;T1/	JCN in question
;T2/	(Extended) Function to call if OK
;T3/	Argument for function
;			Maybe NOINT
;	CALL CHKJCN
;Ret+1:	Always.  T1 has -1,,error or value of FN(JCN,ARG1)
;			-1,,ELP+↑D1  Invalid JCN, No TCB

CHKJCN::PUSH P,T1		; Save the JCN
	CAIL T1,1		; Reasonable number?
	 CAIL T1,MAXJCN
	  JRST CHKJC9		; No.  Tell Caller
	HRRZ TCB,JCNTCB(T1)	; Get the TCB
	JUMPE TCB,CHKJC9	; Non-JCN, give error
	SETSEC TCB,INTSEC
CHKJC1:	LOAD T1,TOWNR,(TCB)
	CAME T1,JOBNO
	  TCPBUG(CHK,<CHKJCN: TCB ownership screwed up>,TCPJS3)
	XMOVEI T1,TCBLCK(TCB)	; Pointer to the connection lock
	MOVE T4,T3		; Put arg in right place
	MOVE T3,0(P)		; Get the JCN as first ARG to function
	CALL LCKCAL		; Lock the lock and call the function
	CAIA			; Use whatever value is returned
CHKJC9:	  HRROI T1,ELP+↑D1	; "Illegal Connection"
	SUB P,BHC+1
	RET



; GETJCN			Assign a Job Connection Number
;			NOINT
;	CALL GETJCN
;Ret+1:	Always.  T1 has the JCN (.GT.0) or -1,,ELT+↑D4

GETJCN::NOSKED			; Prevent others from interfering
	MOVSI T2,-MAXJCN+1	; Max number of JCNs per job (ignore 0)
	SKIPE JCNTCB+1(T2)	; Empty slot?
	  AOBJN T2,.-1		; No.  Check next
	HRROI T1,ELT+↑D4	; "No space for another connection"
	JUMPGE T2,GETJCX	; Return that if no empty slot found
	MOVE T3,FORKX		; Our identity.
	HRLZM T3,JCNTCB+1(T2)	; Reserve the slot for later use
	MOVEI T1,1(T2)		; The JCN as a result.
GETJCX:	OKSKED
	RET

; RETJCN(JCN)			Free a Job Connection Number

;T1/	JCN
;			NOINT
;	CALL RETJCN		; NB T2 preserved
;Ret+1:	Always.

RETJCN::PUSH P,TCB		; Save so we can use this AC
	NOSKED
	CAIN T1,-1		; Job0 w/o JCN?
	  JRST RETJCX		; Yes, special User TVT connection
	CAIL T1,1
	 CAIL T1,MAXJCN		; Reasonable number
	  CAIA
	   JRST RETJC1
	TCPBUG(INF,<RETJCN: JCN out of range>,TCPJS1)
	JRST RETJCX

RETJC1:	SETZ TCB,
	EXCH TCB,JCNTCB(T1)
	TRNN TCB,-1		; Just a reserved slot?
	  JRST RETJCX		; Yes.  Get out.
	SETSEC TCB,INTSEC	; Make extended address
	MOVNI T3,1
	STOR T3,TPSIC,(TCB)	; Disable all PSIs
	STOR T3,TPIFU,(TCB)	; Remove forks from TCB
	STOR T3,TPIFR,(TCB)
	STOR T3,TPIFS,(TCB)
	STOR T3,TPIFE,(TCB)
	STOR T3,TPIFX,(TCB)
	STOR T3,TPIFA,(TCB)
	STOR T3,TOFRK,(TCB)	; Forget owning fork
	SETZRO TJCN,(TCB)	; Disown the TCB ("DEAD")
RETJCX:	OKSKED
	POP P,TCB
	RET

; MAKBFR		Make a buffer descriptor block

; Buffer descriptors ("Buffers") are the items which get queued for the
; Packetizer and Reassembler.  There is one for each SEND or RECV
; executed by the user.  Amoung other things, a buffer block contains
; an "index" which associates that buffer with a particular DONE bit
; which is stored in resident core; it is this bit that the scheduler
; tests to reactivate a process which is waiting for that particular
; buffer.

;TCB/	(ext) pointer to locked connection block
;
;	CALL MAKBFR
;Ret+1:	Always.  T1 has the buffer address (.GT.0) or -1,,error
;		-1,,ELP+↑D15  Count < 0, Adr last word >= 1,,0
;		-1,,ELT+↑D16  No WAIT bits, No memory for BFR HDR

MAKBFR:	STACKL <DATADR>
	LOCAL <HDRADR,FLAGS,COUNT,JCNFLG>
	PUSH P,BFR
	UMOVE JCNFLG,T1		; Get JCN control flags from user
	UMOVE HDRADR,T2		; Get address of header from user
	SUBI HDRADR,BFRSUI	; Make it into standard header ptr.
	MOVSI FLAGS,(TCP%DN!TCP%ER)	; Done and Error bits
	XCTU [ANDCAB FLAGS,BFRFLG(HDRADR)] ; Clear in user space, get others
	TXNE FLAGS,TCP%UR	; Urgent (send) bit on?
	  TXO FLAGS,TCP%PU	; Yes.  That implies a PUSH.
	UMOVE T3,BFRDAD(HDRADR); Address of data area
	MOVEM T3,DATADR
	UMOVE COUNT,BFRCNT(HDRADR); Number of words/bytes in buffer
	JUMPL COUNT,MAKBF9	; Illegal
; ?? Is this used??
	MOVE T1,DATADR
	LSH T1,-PGSFT		; First page of buffer

	MOVE T2,DATADR
	MOVE T3,COUNT
	TLNE JCNFLG,(TCP%WM)	; Count is words?
	  JRST MAKBF1		; Yes.
	ADDI T3,3		; Round up to word boundary
	ASH T3,-2		; Number of words in the buffer
MAKBF1:
	ADD T2,T3
	SUBI T2,1		; Last word in buffer
	LSH T2,-PGSFT		; Last page in buffer
	CAIL T2,1000		; Better fit in memory
	  JRST MAKBF9		; Give error

	TLNN JCNFLG,(TCP%WT)	; Will this fork wait for this buffer?
	 TDZA T1,T1		; No.  No wait bit index assigned
	  CALL ASNWTB		; Assign an index
	JUMPL T1,MAKBFX		; None available right now ??? error code?
	PUSH P,T1		; Save for a while
	SKIPE T1		; No bit to clear
	  CALL CLRWTB		; Clr it to make us hang at SENDW (e.g.)
	MOVEI T1,BFRSIZ		; Size of a buffer descriptor
	CALL GETBLK		; Get a block of free storage
	SKIPG BFR,T1		; Got it? ??? error code?
	  JRST MAKBF8		; No.  Release index and return ELT+↑D16
	SETZM BFRQ(BFR)		; Indicate buffer is not on a queue
	POP P,T1		; Get back the index
	STOR T1,BIDX,(BFR)	; Put in wait bit index
	STOR TCB,BTCB,(BFR)	; Remember which TCB owns the buffer

	MOVEM FLAGS,BFRFLG(BFR)	; Store in monitor copy
	SETZRO BPTR,(BFR)	; Clear Index and Indirect fields
	MOVX T1,↑D8		; Assume byte-send
	TLNE FLAGS,(TCP%WM)	; Word mode?
	  MOVX T1,↑D36		; Yes.  Byte size is 36
	STOR T1,BPTRS,(BFR)	; Set into size field of byte pointer
	MOVE T1,TODCLK		; Now in milliseconds
	STOR T1,BTS,(BFR)	; Set into buffer timestamp
	STOR COUNT,BICNT,(BFR)	; Remember the initial count
	STOR HDRADR,BHADR,(BFR)	; and header address in user space
	MOVE T3,DATADR		; Get the user's data address
	STOR T3,BDADR,(BFR)	; Remember it
	UMOVE T1,BFROPT(HDRADR)	; Get option addresses word
; ****	Beginning of Compatability Kludge
	OPSTR SKIPN,TNUFM,(TCB)	; Using new formats?
	  SETZ T1,		; No, garbage
; ****	End of Compatability Kludge
	MOVEM T1,BFROPT(BFR)	; Save them
	MOVX T1,-1		; "Not mapped" indication
	STOR T1,BMPAG,(BFR)	; In the monitor window page number
	CALL RSTBFR		; Reset the buffer state
	MOVE T1,FORKX		; Our own System Fork Number
	STOR T1,BFRKX,(BFR)	; Remember for mapping user space
	HLRZ T1,FKPGS(T1)	; Our own UPT
	PUSH P,Q1		; Protect critical AC
	CALL UPSHR		; Keep UPT from going away
	POP P,Q1
	MOVE T1,BFR		; This is the value
	JRST MAKBFX

; No space for buffer
MAKBF8:	POP P,T1		; Get back index
; skipe t1??
	TLNE JCNFLG,(TCP%WT)	; Did we assign one?
	  CALL RELWTB		; Release it
	SKIPA T1,[-1,,ELT+↑D16]	; "No space right now"
MAKBF9:	  HRROI T1,ELP+↑D15	; "Bad buffer arg(s)"
MAKBFX:	POP P,BFR
	RESTORE
	RET

; FREBFR(BFR)		Release resources used by a buffer

;			Called by a process doing a SEND, RECV
;			which waits for completion.  In this case USRBFE
;			(or USRBFF) places the complete buffer on the
;			TCPBDQ so it may be release by this routine in
;			the above JSYSs or by ABORT.

;BFR/	(Extended) Buffer
;
;	CALL FREBFR
;Ret+1:	Always

FREBFR:	NOSKED
	LOAD T1,BIDX,(BFR)	; Get the wait bit index
	SETZRO BIDX,(BFR)	; Indicate it has been released
	SKIPE T1		; Have a bit to release?
	  CALL RELWTB		; Actually release it
	MOVE T1,BFR		; Item to dequeue
	SKIPE (T1)		; If not queued, skip it
	  CALL DQ		; Remove it from the done queue
	OKSKED
	CALLRET RETBLK		; Release the storage



	TNXEND